#include "stdafx.h"
#include "blinddel.h"

//
// Create a blind or callback delegator
// punkOuter         Controlling IUnknown
// punkDelegatee     vtable being wrapped
// piid              iid to QI for, or NULL if punkDelegatee is the correct vtable
// pEmbeddedThis     A pointer to a _BlindDelegator struct that has already been created.
// pfnDestroy        A function pointer used to destroy the value in pEmbeddedThis
// ppvObj            output parameter
//
HRESULT _BlindDelegator::CreateDelegator(IUnknown *punkOuter, IUnknown *punkDelegatee, const IID* piid, _BlindDelegator* pEmbeddedThis, DestroyBlindDelegator pfnDestroy, IUnknown **ppvObj)
{
    IUnknown *pItf;
    HRESULT hr;
    *ppvObj = NULL;
    if (piid)
    {
        // Make sure we're delegating to the correct interface
        hr = punkDelegatee->QueryInterface(*piid, (void**)&pItf);
    }
    else
    {
        // The correct interface is being passed in, no need to QI
        (pItf = punkDelegatee)->AddRef();
        hr = NOERROR;
    }
    if (SUCCEEDED(hr))
    {
        _BlindDelegator *pDel = pEmbeddedThis;
        if (pDel)
        {
            // We must have some value for the a function pointer
            // because destruction keys off this value to determine
            // if we're embedded or heap allocated.
            if (pfnDestroy == NULL)
            {
                pfnDestroy = (DestroyBlindDelegator)-1;
            }
        }
        else if (pDel = new _BlindDelegator())
        {
            ADD_OBJECT;
        }
        if (pDel)
        {
            pDel->m_dwRefs = 1;
            (pDel->m_punkOuter = punkOuter)->AddRef();
            pDel->m_punkInner = pItf;
            pDel->m_pVTable = &g_bdvtbl;
            pDel->m_pfnDestroy = pfnDestroy;
            *ppvObj = (IUnknown*)(LPBYTE(pDel) + offsetof(_BlindDelegator, m_pVTable));
        }
        else
        {
            pItf->Release();
            hr = E_OUTOFMEMORY;
        }
    }
    return hr;
}

            
ULONG _BlindDelegator::DestroyThis()
{
    IUnknown *punkOuter = m_punkOuter;
    ULONG retVal = 0;
    if (m_pfnDestroy)
    {
        IUnknown *punkInner = m_punkInner;
        m_punkOuter = NULL;
        m_punkInner = NULL;
        if ((long)m_pfnDestroy != -1)
        {
            retVal = m_pfnDestroy(this, &punkInner, &punkOuter);
        }
        if (punkInner)
        {
            punkInner->Release();
        }
        if (punkOuter)
        {
            retVal = punkOuter->Release();
        }
    }
    else
    {
        m_punkInner->Release();
        delete this;
        REMOVE_OBJECT;
        retVal = punkOuter->Release();
    }
    return retVal;
}

__declspec(naked) void BDQueryInterface()
{
    _asm
    {
        mov eax, DWORD PTR [esp + 4]   // move BD::this into eax
        mov eax, DWORD PTR [eax + 8]   // move BD::this->m_punkOuter into eax
        mov DWORD PTR [esp + 4], eax   // move eax into "this" position on stack
        mov eax, DWORD PTR [eax]       // move base of vtable into eax
        jmp DWORD PTR [eax]            // jmp to the actual function, which is eax + function number
    }
}
__declspec(naked) void Thunk()
{
    _asm
    {
        mov eax, DWORD PTR [esp + 4]   // move BD::this into eax
        mov eax, DWORD PTR [eax + 4]   // move BD::this->m_punkInner into eax
        mov [esp + 4], eax             // move eax into "this" position on stack
        mov eax, DWORD PTR [eax]       // move base of vtable into eax
        jmp DWORD PTR [eax + ecx]      // jmp to the actual function, which is eax + function number
    }
}

enum { SZPFN = sizeof(void (*)()) };

#define METHODTHUNK(n) void __declspec(naked) methodthunk##n() { _asm { mov ecx, (n * SZPFN) } _asm { jmp Thunk } }

#define METHODTHUNK10(n10) \
    METHODTHUNK(n10##0) \
    METHODTHUNK(n10##1) \
    METHODTHUNK(n10##2) \
    METHODTHUNK(n10##3) \
    METHODTHUNK(n10##4) \
    METHODTHUNK(n10##5) \
    METHODTHUNK(n10##6) \
    METHODTHUNK(n10##7) \
    METHODTHUNK(n10##8) \
    METHODTHUNK(n10##9) \

#define METHODTHUNK100(n100) \
    METHODTHUNK10(n100##0) \
    METHODTHUNK10(n100##1) \
    METHODTHUNK10(n100##2) \
    METHODTHUNK10(n100##3) \
    METHODTHUNK10(n100##4) \
    METHODTHUNK10(n100##5) \
    METHODTHUNK10(n100##6) \
    METHODTHUNK10(n100##7) \
    METHODTHUNK10(n100##8) \
    METHODTHUNK10(n100##9) \


METHODTHUNK(3)
METHODTHUNK(4)
METHODTHUNK(5)
METHODTHUNK(6)
METHODTHUNK(7)
METHODTHUNK(8)
METHODTHUNK(9)
METHODTHUNK10(1)
METHODTHUNK10(2)
METHODTHUNK10(3)
METHODTHUNK10(4)
METHODTHUNK10(5)
METHODTHUNK10(6)
METHODTHUNK10(7)
METHODTHUNK10(8)
METHODTHUNK10(9)
METHODTHUNK100(1)
METHODTHUNK100(2)
METHODTHUNK100(3)
METHODTHUNK100(4)
METHODTHUNK100(5)
METHODTHUNK100(6)
METHODTHUNK100(7)
METHODTHUNK100(8)
METHODTHUNK100(9)
METHODTHUNK10(100)
METHODTHUNK10(101)
METHODTHUNK(1020)
METHODTHUNK(1021)
METHODTHUNK(1022)
METHODTHUNK(1023)


#define UMTHUNK(n) reinterpret_cast<VTBL_ENTRY>(methodthunk##n), 

#define UMTHUNK10(n) \
    UMTHUNK(n##0) \
    UMTHUNK(n##1) \
    UMTHUNK(n##2) \
    UMTHUNK(n##3) \
    UMTHUNK(n##4) \
    UMTHUNK(n##5) \
    UMTHUNK(n##6) \
    UMTHUNK(n##7) \
    UMTHUNK(n##8) \
    UMTHUNK(n##9) \

#define UMTHUNK100(n) \
    UMTHUNK10(n##0) \
    UMTHUNK10(n##1) \
    UMTHUNK10(n##2) \
    UMTHUNK10(n##3) \
    UMTHUNK10(n##4) \
    UMTHUNK10(n##5) \
    UMTHUNK10(n##6) \
    UMTHUNK10(n##7) \
    UMTHUNK10(n##8) \
    UMTHUNK10(n##9) \



const VTABLE g_bdvtbl = {
    //reinterpret_cast<VTBL_ENTRY>(_BlindDelegator::QueryInterface),
    reinterpret_cast<VTBL_ENTRY>(BDQueryInterface),
    reinterpret_cast<VTBL_ENTRY>(_BlindDelegator::AddRef),
    reinterpret_cast<VTBL_ENTRY>(_BlindDelegator::Release),
    UMTHUNK(3)
    UMTHUNK(4)
    UMTHUNK(5)
    UMTHUNK(6)
    UMTHUNK(7)
    UMTHUNK(8)
    UMTHUNK(9)
    UMTHUNK10(1)
    UMTHUNK10(2)
    UMTHUNK10(3)
    UMTHUNK10(4)
    UMTHUNK10(5)
    UMTHUNK10(6)
    UMTHUNK10(7)
    UMTHUNK10(8)
    UMTHUNK10(9)
    UMTHUNK100(1)
    UMTHUNK100(2)
    UMTHUNK100(3)
    UMTHUNK100(4)
    UMTHUNK100(5)
    UMTHUNK100(6)
    UMTHUNK100(7)
    UMTHUNK100(8)
    UMTHUNK100(9)
    UMTHUNK10(100)
    UMTHUNK10(101)
    UMTHUNK(1020)
    UMTHUNK(1021)
    UMTHUNK(1022)
    UMTHUNK(1023)
};

